import {
  WebSocketGateway,
  WebSocketServer,
  SubscribeMessage,
  OnGatewayConnection,
  OnGatewayDisconnect,
  MessageBody,
  ConnectedSocket,
} from '@nestjs/websockets';
import { Server, Socket } from 'socket.io';
import { Logger } from '@nestjs/common';
import { HeliusService } from '../services/helius.service';
import { DatabaseService } from '../services/database.service';

interface ClientInfo {
  socketId: string;
  userPublicKey?: string;
  subscribedTokens: Set<string>;
  subscribedAddresses: Set<string>;
  // 聊天相关信息
  username?: string;
  isInChat: boolean;
  joinedChatAt?: Date;
}

@WebSocketGateway({
  cors: {
    origin: (origin, callback) => {
      // 在开发环境中允许所有来源
      if (process.env.NODE_ENV === 'development') {
        callback(null, true);
        return;
      }

      // 在生产环境中允许特定的来源
      const allowedOrigins = [
        'http://localhost:7171',
        'http://104.129.59.158:7171',
        'https://c4pad.fun',
        'http://104.129.59.158',
        'https://104.129.59.158',
        process.env.WEBSOCKET_CORS_ORIGIN,
        process.env.FRONTEND_URL,
      ].filter(Boolean);

      if (!origin || allowedOrigins.includes(origin)) {
        callback(null, true);
      } else {
        callback(new Error('Not allowed by CORS'));
      }
    },
    credentials: true,
  },
  namespace: '/realtime',
})
export class RealtimeGateway
  implements OnGatewayConnection, OnGatewayDisconnect
{
  @WebSocketServer()
  server: Server;

  private readonly logger = new Logger(RealtimeGateway.name);
  private clients = new Map<string, ClientInfo>();
  private heliusSubscriptions = new Map<string, number>();

  // 聊天相关存储
  private chatMessages: Array<{
    id: string;
    username: string;
    message: string;
    timestamp: string;
    isSystem?: boolean;
  }> = [];

  private chatUsers = new Map<
    string,
    {
      id: string;
      username: string;
      isOnline: boolean;
      joinedAt: string;
    }
  >();

  constructor(
    private readonly heliusService: HeliusService,
    private readonly database: DatabaseService,
  ) {}

  async handleConnection(client: Socket) {
    const clientInfo: ClientInfo = {
      socketId: client.id,
      subscribedTokens: new Set(),
      subscribedAddresses: new Set(),
      isInChat: false,
    };

    this.clients.set(client.id, clientInfo);

    // 保存连接到数据库
    try {
      await this.database.webSocketConnection.create({
        data: {
          socketId: client.id,
          connectedAt: new Date(),
          lastSeen: new Date(),
        },
      });
    } catch (error) {
      this.logger.error('Failed to save connection to database:', error);
    }

    // 记录连接详细信息
    this.logger.log(
      `Client connected: ${client.id}, Origin: ${client.request.headers.origin}, User-Agent: ${client.request.headers['user-agent']}`,
    );
    this.logger.log(`Total connected clients: ${this.clients.size}`);

    // 发送连接确认
    client.emit('connected', {
      message: 'Connected to Ignite Fun realtime server',
      timestamp: new Date().toISOString(),
    });
  }

  async handleDisconnect(client: Socket) {
    const clientInfo = this.clients.get(client.id);

    if (clientInfo) {
      // 处理聊天相关的清理
      this.handleChatDisconnection(client);

      // 取消所有订阅
      for (const address of clientInfo.subscribedAddresses) {
        await this.unsubscribeFromAddress(address);
      }

      this.clients.delete(client.id);
    }

    // 更新数据库连接状态
    try {
      await this.database.webSocketConnection.updateMany({
        where: { socketId: client.id },
        data: { isActive: false },
      });
    } catch (error) {
      this.logger.error('Failed to update connection status:', error);
    }

    this.logger.log(`Client disconnected: ${client.id}`);
    this.logger.log(`Total connected clients: ${this.clients.size}`);
  }

  /**
   * 客户端身份验证
   */
  @SubscribeMessage('authenticate')
  async handleAuthenticate(
    @MessageBody() data: { publicKey: string },
    @ConnectedSocket() client: Socket,
  ) {
    try {
      // 验证公钥格式
      if (!this.heliusService.isValidAddress(data.publicKey)) {
        client.emit('error', { message: 'Invalid public key' });
        return;
      }

      const clientInfo = this.clients.get(client.id);
      if (clientInfo) {
        clientInfo.userPublicKey = data.publicKey;
      }

      // 更新数据库
      await this.database.webSocketConnection.updateMany({
        where: { socketId: client.id },
        data: { userPublicKey: data.publicKey },
      });

      client.emit('authenticated', {
        publicKey: data.publicKey,
        timestamp: new Date().toISOString(),
      });

      this.logger.log(
        `Client authenticated: ${client.id} -> ${data.publicKey}`,
      );
    } catch (error) {
      this.logger.error('Authentication failed:', error);
      client.emit('error', { message: 'Authentication failed' });
    }
  }

  /**
   * 订阅代币更新
   */
  @SubscribeMessage('subscribe_token')
  async handleSubscribeToken(
    @MessageBody() data: { mint: string },
    @ConnectedSocket() client: Socket,
  ) {
    try {
      const clientInfo = this.clients.get(client.id);
      if (!clientInfo) {
        client.emit('error', { message: 'Client not found' });
        return;
      }

      clientInfo.subscribedTokens.add(data.mint);

      // 加入代币房间
      await client.join(`token:${data.mint}`);

      client.emit('subscribed', {
        type: 'token',
        mint: data.mint,
        timestamp: new Date().toISOString(),
      });

      this.logger.log(`Client ${client.id} subscribed to token: ${data.mint}`);
    } catch (error) {
      this.logger.error('Failed to subscribe to token:', error);
      client.emit('error', { message: 'Failed to subscribe to token' });
    }
  }

  /**
   * 取消订阅代币
   */
  @SubscribeMessage('unsubscribe_token')
  async handleUnsubscribeToken(
    @MessageBody() data: { mint: string },
    @ConnectedSocket() client: Socket,
  ) {
    try {
      const clientInfo = this.clients.get(client.id);
      if (!clientInfo) {
        return;
      }

      clientInfo.subscribedTokens.delete(data.mint);

      // 离开代币房间
      await client.leave(`token:${data.mint}`);

      client.emit('unsubscribed', {
        type: 'token',
        mint: data.mint,
        timestamp: new Date().toISOString(),
      });

      this.logger.log(
        `Client ${client.id} unsubscribed from token: ${data.mint}`,
      );
    } catch (error) {
      this.logger.error('Failed to unsubscribe from token:', error);
    }
  }

  /**
   * 订阅地址变化
   */
  @SubscribeMessage('subscribe_address')
  async handleSubscribeAddress(
    @MessageBody() data: { address: string },
    @ConnectedSocket() client: Socket,
  ) {
    try {
      if (!this.heliusService.isValidAddress(data.address)) {
        client.emit('error', { message: 'Invalid address' });
        return;
      }

      const clientInfo = this.clients.get(client.id);
      if (!clientInfo) {
        client.emit('error', { message: 'Client not found' });
        return;
      }

      clientInfo.subscribedAddresses.add(data.address);

      // 如果这是第一次订阅这个地址，创建 Helius 订阅
      if (!this.heliusSubscriptions.has(data.address)) {
        const subscriptionId = await this.heliusService.subscribeToAccount(
          data.address,
          (accountInfo) => {
            this.broadcastAccountChange(data.address, accountInfo);
          },
        );
        this.heliusSubscriptions.set(data.address, subscriptionId);
      }

      // 加入地址房间
      await client.join(`address:${data.address}`);

      client.emit('subscribed', {
        type: 'address',
        address: data.address,
        timestamp: new Date().toISOString(),
      });

      this.logger.log(
        `Client ${client.id} subscribed to address: ${data.address}`,
      );
    } catch (error) {
      this.logger.error('Failed to subscribe to address:', error);
      client.emit('error', { message: 'Failed to subscribe to address' });
    }
  }

  /**
   * 广播账户变化
   */
  private broadcastAccountChange(address: string, accountInfo: any) {
    this.server.to(`address:${address}`).emit('account_changed', {
      address,
      accountInfo,
      timestamp: new Date().toISOString(),
    });
  }

  /**
   * 广播代币价格更新
   */
  async broadcastTokenPriceUpdate(
    mint: string,
    price: number,
    marketCap: number,
  ) {
    const updateData = {
      mint,
      price,
      marketCap,
      timestamp: new Date().toISOString(),
    };

    // 发送给订阅了特定代币的客户端
    this.server.to(`token:${mint}`).emit('token_price_update', updateData);
    
    // 同时发送全局价格更新，供代币列表等组件使用
    this.server.emit('token_price_update', updateData);
  }

  /**
   * 广播新交易
   */
  async broadcastNewTransaction(transaction: any) {
    // 广播给所有客户端
    this.server.emit('new_transaction', {
      transaction,
      timestamp: new Date().toISOString(),
    });

    // 如果交易涉及特定代币，也广播给代币订阅者
    if (transaction.tokenMint) {
      this.server
        .to(`token:${transaction.tokenMint}`)
        .emit('token_transaction', {
          transaction,
          timestamp: new Date().toISOString(),
        });
    }
  }

  /**
   * 广播新代币创建
   */
  async broadcastNewToken(token: any) {
    this.server.emit('new_token', {
      token,
      timestamp: new Date().toISOString(),
    });
  }

  /**
   * 取消地址订阅
   */
  private async unsubscribeFromAddress(address: string) {
    const subscriptionId = this.heliusSubscriptions.get(address);
    if (subscriptionId) {
      try {
        await this.heliusService.unsubscribeFromAccount(subscriptionId);
        this.heliusSubscriptions.delete(address);
      } catch (error) {
        this.logger.error(
          `Failed to unsubscribe from address ${address}:`,
          error,
        );
      }
    }
  }

  /**
   * 获取在线用户数量
   */
  getOnlineUsersCount(): number {
    return this.clients.size;
  }

  /**
   * 获取房间信息
   */
  getRoomInfo(room: string): number {
    const roomSockets = this.server.sockets.adapter.rooms.get(room);
    return roomSockets ? roomSockets.size : 0;
  }

  // ================================
  // 聊天功能
  // ================================

  /**
   * 用户加入聊天
   */
  @SubscribeMessage('join_chat')
  async handleJoinChat(
    @MessageBody() data: { username: string },
    @ConnectedSocket() client: Socket,
  ) {
    const clientInfo = this.clients.get(client.id);
    if (!clientInfo) return;

    const { username } = data;

    // 检查用户名是否已被使用
    const isUsernameTaken = Array.from(this.chatUsers.values()).some(
      (user) => user.username === username && user.isOnline,
    );

    if (isUsernameTaken) {
      client.emit('username_taken', {
        message: '用户名已被使用，请选择其他名称',
      });
      return;
    }

    // 如果用户之前已经在聊天中，先移除旧的信息
    if (clientInfo.username && this.chatUsers.has(clientInfo.username)) {
      this.chatUsers.delete(clientInfo.username);
    }

    // 更新客户端信息
    clientInfo.username = username;
    clientInfo.isInChat = true;
    clientInfo.joinedChatAt = new Date();

    // 添加到聊天用户列表
    const chatUser = {
      id: client.id,
      username,
      isOnline: true,
      joinedAt: new Date().toISOString(),
    };
    this.chatUsers.set(username, chatUser);

    // 将客户端加入聊天房间
    client.join('chat');

    // 广播用户加入消息
    this.server.to('chat').emit('user_joined', {
      user: chatUser,
      timestamp: new Date().toISOString(),
    });

    // 发送聊天历史记录给新加入的用户
    client.emit('chat_history', {
      messages: this.chatMessages.slice(-50), // 只发送最近50条消息
      timestamp: new Date().toISOString(),
    });

    // 发送当前在线用户列表
    const onlineUsers = Array.from(this.chatUsers.values()).filter(
      (user) => user.isOnline,
    );
    this.server.to('chat').emit('online_users', {
      users: onlineUsers,
      timestamp: new Date().toISOString(),
    });

    this.logger.log(`用户 ${username} 加入聊天`);
  }

  /**
   * 发送聊天消息
   */
  @SubscribeMessage('send_message')
  async handleSendMessage(
    @MessageBody() data: { message: string },
    @ConnectedSocket() client: Socket,
  ) {
    const clientInfo = this.clients.get(client.id);
    if (!clientInfo || !clientInfo.isInChat || !clientInfo.username) {
      return;
    }

    const { message } = data;
    if (!message.trim()) return;

    // 创建消息对象
    const chatMessage = {
      id: `msg-${Date.now()}-${client.id}`,
      username: clientInfo.username,
      message: message.trim(),
      timestamp: new Date().toISOString(),
    };

    // 存储消息（限制最多1000条）
    this.chatMessages.push(chatMessage);
    if (this.chatMessages.length > 1000) {
      this.chatMessages = this.chatMessages.slice(-1000);
    }

    // 广播消息给聊天房间中的所有用户
    this.server.to('chat').emit('new_message', {
      message: chatMessage,
      timestamp: new Date().toISOString(),
    });

    this.logger.log(
      `用户 ${clientInfo.username} 发送消息: ${message.substring(0, 50)}...`,
    );
  }

  /**
   * 用户离开聊天
   */
  @SubscribeMessage('leave_chat')
  async handleLeaveChat(@ConnectedSocket() client: Socket) {
    const clientInfo = this.clients.get(client.id);
    if (!clientInfo || !clientInfo.username) return;

    const username = clientInfo.username;
    const chatUser = this.chatUsers.get(username);

    if (chatUser) {
      // 标记用户为离线
      chatUser.isOnline = false;
      this.chatUsers.delete(username);

      // 离开聊天房间
      client.leave('chat');

      // 广播用户离开消息
      this.server.to('chat').emit('user_left', {
        user: chatUser,
        timestamp: new Date().toISOString(),
      });

      // 更新在线用户列表
      const onlineUsers = Array.from(this.chatUsers.values()).filter(
        (user) => user.isOnline,
      );
      this.server.to('chat').emit('online_users', {
        users: onlineUsers,
        timestamp: new Date().toISOString(),
      });

      this.logger.log(`用户 ${username} 离开聊天`);
    }

    // 更新客户端信息
    clientInfo.username = undefined;
    clientInfo.isInChat = false;
    clientInfo.joinedChatAt = undefined;
  }

  /**
   * 处理客户端断开连接时的聊天清理
   */
  private handleChatDisconnection(client: Socket) {
    const clientInfo = this.clients.get(client.id);
    if (clientInfo && clientInfo.isInChat && clientInfo.username) {
      this.handleLeaveChat(client);
    }
  }
}
